#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
基于FFmpeg的音视频播放器
提供音视频同步播放功能
"""

import os
import sys
import time
import threading
import subprocess
import numpy as np
from typing import Optional, Callable
from PySide6.QtCore import QObject, Signal, QTimer
from PySide6.QtGui import QImage, QPixmap
import ffmpeg
import cv2


class FFmpegPlayer(QObject):
    """
    基于FFmpeg的音视频播放器
    
    使用FFmpeg进程来处理音视频播放，支持音视频同步，
    通过信号机制与GUI界面通信。
    """
    
    # 信号定义
    frame_ready = Signal(QImage)  # 新帧准备就绪
    position_changed = Signal(float)  # 播放位置改变
    duration_changed = Signal(float)  # 视频时长改变
    playback_finished = Signal()  # 播放结束
    error_occurred = Signal(str)  # 发生错误
    
    def __init__(self):
        super().__init__()
        self.video_path: Optional[str] = None
        self.duration: float = 0.0
        self.current_position: float = 0.0
        self.is_playing: bool = False
        self.is_paused: bool = False
        
        # FFmpeg进程相关
        self.ffmpeg_process: Optional[subprocess.Popen] = None
        self.audio_process: Optional[subprocess.Popen] = None
        
        # 播放控制
        self.frame_timer = QTimer()
        self.frame_timer.timeout.connect(self._update_frame)
        self.target_fps: float = 30.0
        
        # 线程控制
        self.playback_thread: Optional[threading.Thread] = None
        self.stop_playback: bool = False
        
    def load_video(self, video_path: str) -> bool:
        """
        加载视频文件
        
        Args:
            video_path (str): 视频文件路径
            
        Returns:
            bool: 加载成功返回True
        """
        if not os.path.exists(video_path):
            self.error_occurred.emit(f"视频文件不存在: {video_path}")
            return False
            
        try:
            # 获取视频信息
            probe = ffmpeg.probe(video_path)
            video_stream = next((stream for stream in probe['streams'] 
                               if stream['codec_type'] == 'video'), None)
            
            if video_stream is None:
                self.error_occurred.emit("未找到视频流")
                return False
                
            # 获取视频时长和帧率
            self.duration = float(probe['format'].get('duration', 0))
            
            # 获取帧率
            fps = 30.0
            if 'avg_frame_rate' in video_stream:
                try:
                    fps_fraction = video_stream['avg_frame_rate']
                    if isinstance(fps_fraction, str) and '/' in fps_fraction:
                        num, den = fps_fraction.split('/')
                        if float(den) != 0:
                            fps = float(num) / float(den)
                    else:
                        fps = float(fps_fraction)
                except:
                    pass
                    
            self.target_fps = fps
            self.video_path = video_path
            self.current_position = 0.0
            
            # 发送信号
            self.duration_changed.emit(self.duration)
            self.position_changed.emit(0.0)
            
            return True
            
        except Exception as e:
            self.error_occurred.emit(f"加载视频失败: {str(e)}")
            return False
    
    def play(self):
        """开始播放"""
        if not self.video_path:
            return
            
        if self.is_paused:
            # 从暂停状态恢复
            self.is_paused = False
            self.is_playing = True
            self._resume_playback()
        else:
            # 开始新的播放
            self.is_playing = True
            self.is_paused = False
            self._start_playback()
    
    def pause(self):
        """暂停播放"""
        if self.is_playing:
            self.is_paused = True
            self.is_playing = False
            self._pause_playback()
    
    def stop(self):
        """停止播放"""
        self.is_playing = False
        self.is_paused = False
        self.stop_playback = True
        self._stop_playback()
        self.current_position = 0.0
        self.position_changed.emit(0.0)
    
    def seek(self, position: float):
        """跳转到指定位置
        
        Args:
            position (float): 目标位置（秒）
        """
        if not self.video_path or position < 0 or position > self.duration:
            return
            
        self.current_position = position
        self.position_changed.emit(position)
        
        if self.is_playing:
            # 重新开始播放
            self._stop_playback()
            self._start_playback()
    
    def _start_playback(self):
        """开始播放进程"""
        self._stop_playback()  # 确保之前的进程已停止
        
        self.stop_playback = False
        
        # 启动播放线程
        self.playback_thread = threading.Thread(target=self._playback_worker)
        self.playback_thread.daemon = True
        self.playback_thread.start()
    
    def _playback_worker(self):
        """播放工作线程"""
        try:
            # 创建视频流进程
            video_input = ffmpeg.input(self.video_path, ss=self.current_position)
            video_output = ffmpeg.output(
                video_input,
                'pipe:',
                format='rawvideo',
                pix_fmt='rgb24',
                loglevel='quiet'
            )
            
            # 创建音频播放进程（使用系统播放器）
            if sys.platform == "darwin":  # macOS
                audio_cmd = [
                    'ffplay',
                    '-nodisp',
                    '-autoexit',
                    '-ss', str(self.current_position),
                    self.video_path
                ]
            else:  # Linux/Windows
                audio_cmd = [
                    'ffplay',
                    '-nodisp',
                    '-autoexit',
                    '-ss', str(self.current_position),
                    self.video_path
                ]
            
            # 启动音频播放
            try:
                self.audio_process = subprocess.Popen(
                    audio_cmd,
                    stdout=subprocess.DEVNULL,
                    stderr=subprocess.DEVNULL
                )
            except FileNotFoundError:
                # 如果没有ffplay，只播放视频
                print("警告: 未找到ffplay，只播放视频")
                self.audio_process = None
            
            # 启动视频流
            self.ffmpeg_process = ffmpeg.run_async(
                video_output,
                pipe_stdout=True,
                pipe_stderr=subprocess.DEVNULL
            )
            
            # 获取视频信息
            probe = ffmpeg.probe(self.video_path)
            video_stream = next(stream for stream in probe['streams'] 
                              if stream['codec_type'] == 'video')
            width = int(video_stream['width'])
            height = int(video_stream['height'])
            
            frame_size = width * height * 3  # RGB24
            frame_interval = 1.0 / self.target_fps
            
            start_time = time.time()
            frame_count = 0
            
            while not self.stop_playback and self.is_playing:
                # 读取帧数据
                frame_data = self.ffmpeg_process.stdout.read(frame_size)
                if len(frame_data) != frame_size:
                    # 播放结束
                    break
                
                # 转换为numpy数组
                frame = np.frombuffer(frame_data, np.uint8).reshape([height, width, 3])
                
                # 转换为QImage
                qimage = QImage(frame.data, width, height, QImage.Format_RGB888)
                
                # 发送帧信号
                self.frame_ready.emit(qimage)
                
                # 更新播放位置
                frame_count += 1
                expected_time = start_time + frame_count * frame_interval
                current_time = time.time()
                
                # 同步控制
                if current_time < expected_time:
                    time.sleep(expected_time - current_time)
                
                # 更新位置
                self.current_position += frame_interval
                self.position_changed.emit(self.current_position)
                
                # 检查是否播放完成
                if self.current_position >= self.duration:
                    break
            
            # 播放结束
            self.playback_finished.emit()
            
        except Exception as e:
            self.error_occurred.emit(f"播放错误: {str(e)}")
        finally:
            self._cleanup_processes()
    
    def _pause_playback(self):
        """暂停播放"""
        if self.audio_process:
            try:
                self.audio_process.terminate()
            except:
                pass
    
    def _resume_playback(self):
        """恢复播放"""
        self._start_playback()
    
    def _stop_playback(self):
        """停止播放进程"""
        self.stop_playback = True
        self._cleanup_processes()
        
        if self.playback_thread and self.playback_thread.is_alive():
            self.playback_thread.join(timeout=1.0)
    
    def _cleanup_processes(self):
        """清理播放进程"""
        if self.ffmpeg_process:
            try:
                self.ffmpeg_process.terminate()
                self.ffmpeg_process.wait(timeout=1.0)
            except:
                try:
                    self.ffmpeg_process.kill()
                except:
                    pass
            self.ffmpeg_process = None
        
        if self.audio_process:
            try:
                self.audio_process.terminate()
                self.audio_process.wait(timeout=1.0)
            except:
                try:
                    self.audio_process.kill()
                except:
                    pass
            self.audio_process = None
    
    def _update_frame(self):
        """更新帧（定时器回调）"""
        # 这个方法现在由播放线程处理
        pass
    
    def get_current_position(self) -> float:
        """获取当前播放位置"""
        return self.current_position
    
    def get_duration(self) -> float:
        """获取视频总时长"""
        return self.duration
    
    def is_playing_video(self) -> bool:
        """检查是否正在播放"""
        return self.is_playing and not self.is_paused